home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2000 September / september_2000.iso / intercd / root / ^Linux / WindowMaker / src / placement.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-03-29  |  15.7 KB  |  625 lines

  1. /* placement.c - window and icon placement on screen
  2.  * 
  3.  *  Window Maker window manager
  4.  * 
  5.  *  Copyright (c) 1997, 1998 Alfredo K. Kojima
  6.  * 
  7.  *  This program is free software; you can redistribute it and/or modify
  8.  *  it under the terms of the GNU General Public License as published by
  9.  *  the Free Software Foundation; either version 2 of the License, or
  10.  *  (at your option) any later version.
  11.  *
  12.  *  This program is distributed in the hope that it will be useful,
  13.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.  *  GNU General Public License for more details.
  16.  *
  17.  *  You should have received a copy of the GNU General Public License
  18.  *  along with this program; if not, write to the Free Software
  19.  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
  20.  *  USA.
  21.  */
  22.  
  23. #include "wconfig.h"
  24.  
  25. #include <X11/Xlib.h>
  26. #include <stdlib.h>
  27. #include <stdio.h>
  28. #include <string.h>
  29. #include <limits.h>
  30.  
  31. #include "WindowMaker.h"
  32. #include "wcore.h"
  33. #include "framewin.h"
  34. #include "window.h"
  35. #include "icon.h"
  36. #include "appicon.h"
  37. #include "actions.h"
  38. #include "funcs.h"
  39. #include "application.h"
  40. #include "appicon.h"
  41. #include "dock.h"
  42.  
  43.  
  44. extern WPreferences wPreferences;
  45.  
  46.  
  47. #define X_ORIGIN(scr) WMAX((scr)->totalUsableArea.x1,\
  48.                 wPreferences.window_place_origin.x)
  49.  
  50. #define Y_ORIGIN(scr) WMAX((scr)->totalUsableArea.y1,\
  51.                 wPreferences.window_place_origin.y)
  52.  
  53.  
  54. /*
  55.  * interactive window placement is in moveres.c
  56.  */
  57.  
  58. extern void
  59. InteractivePlaceWindow(WWindow *wwin, int *x_ret, int *y_ret, 
  60.                unsigned width, unsigned height);
  61.  
  62.  
  63. /*
  64.  * Returns True if it is an icon and is in this workspace.
  65.  */
  66. static Bool
  67. iconPosition(WCoreWindow *wcore, int sx1, int sy1, int sx2, int sy2, 
  68.          int workspace, int *retX, int *retY)
  69. {
  70.     void *parent;
  71.     int ok = 0;
  72.  
  73.     parent = wcore->descriptor.parent;
  74.  
  75.     /* if it is an application icon */
  76.     if (wcore->descriptor.parent_type == WCLASS_APPICON
  77.     && !((WAppIcon*)parent)->docked) {
  78.     *retX = ((WAppIcon*)parent)->x_pos;
  79.     *retY = ((WAppIcon*)parent)->y_pos;
  80.  
  81.     ok = 1;
  82.     } else if (wcore->descriptor.parent_type == WCLASS_MINIWINDOW &&
  83.            (((WIcon*)parent)->owner->frame->workspace == workspace
  84.         || IS_OMNIPRESENT(((WIcon*)parent)->owner)
  85.         || wPreferences.sticky_icons)
  86.            && ((WIcon*)parent)->mapped) {
  87.  
  88.     *retX = ((WIcon*)parent)->owner->icon_x;
  89.     *retY = ((WIcon*)parent)->owner->icon_y;
  90.  
  91.     ok = 1;
  92.     } else if (wcore->descriptor.parent_type == WCLASS_WINDOW
  93.            && ((WWindow*)parent)->flags.icon_moved
  94.            && (((WWindow*)parent)->frame->workspace == workspace
  95.            || IS_OMNIPRESENT((WWindow*)parent)
  96.            || wPreferences.sticky_icons)) {
  97.     *retX = ((WWindow*)parent)->icon_x;
  98.     *retY = ((WWindow*)parent)->icon_y;
  99.  
  100.     ok = 1;    
  101.     }
  102.         
  103.  
  104.     /*
  105.      * Check if it is inside the screen.
  106.      */
  107.     if (ok) {
  108.     if (*retX < sx1-wPreferences.icon_size)
  109.         ok = 0;
  110.     else if (*retX > sx2)
  111.         ok = 0;
  112.  
  113.     if (*retY < sy1-wPreferences.icon_size)
  114.         ok = 0;
  115.     else if (*retY > sy2)
  116.         ok = 0;
  117.     }
  118.  
  119.     return ok;
  120. }
  121.  
  122.  
  123.  
  124.  
  125. void
  126. PlaceIcon(WScreen *scr, int *x_ret, int *y_ret)
  127. {
  128.     int pf;                   /* primary axis */
  129.     int sf;                   /* secondary axis */
  130.     int fullW;
  131.     int fullH;
  132.     char *map;
  133.     int pi, si;
  134.     WCoreWindow *obj;
  135.     int sx1, sx2, sy1, sy2;           /* screen boundary */
  136.     int sw, sh;
  137.     int xo, yo;
  138.     int xs, ys;
  139.     int x, y;
  140.     int isize = wPreferences.icon_size;
  141.     int done = 0;
  142.     WMBagIterator iter;
  143.  
  144.     /*
  145.      * Find out screen boundaries.
  146.      */
  147.     sx1 = 0;
  148.     sy1 = 0;
  149.     sx2 = scr->scr_width;
  150.     sy2 = scr->scr_height;
  151.     if (scr->dock) {
  152.     if (scr->dock->on_right_side)
  153.             sx2 -= isize + DOCK_EXTRA_SPACE;
  154.     else
  155.         sx1 += isize + DOCK_EXTRA_SPACE;
  156.     }
  157.  
  158.     sw = isize * (scr->scr_width/isize);
  159.     sh = isize * (scr->scr_height/isize);
  160.     fullW = (sx2-sx1)/isize;
  161.     fullH = (sy2-sy1)/isize;
  162.  
  163.     /* icon yard boundaries */    
  164.     if (wPreferences.icon_yard & IY_VERT) {
  165.     pf = fullH;
  166.     sf = fullW;
  167.     } else {
  168.     pf = fullW;
  169.     sf = fullH;
  170.     }
  171.     if (wPreferences.icon_yard & IY_RIGHT) {
  172.     xo = sx2 - isize;
  173.     xs = -1;
  174.     } else {
  175.     xo = sx1;
  176.     xs = 1;
  177.     }
  178.     if (wPreferences.icon_yard & IY_TOP) {
  179.     yo = sy1;
  180.     ys = 1;
  181.     } else {
  182.     yo = sy2 - isize;
  183.     ys = -1;
  184.     }
  185.  
  186.     /*
  187.      * Create a map with the occupied slots. 1 means the slot is used
  188.      * or at least partially used. 
  189.      * The slot usage can be optimized by only marking fully used slots
  190.      * or slots that have most of it covered. 
  191.      * Space usage is worse than the fvwm algorithm (used in the old version)
  192.      * but complexity is much better (faster) than it.
  193.      */
  194.     map = wmalloc((sw+2) * (sh+2));
  195.     memset(map, 0, (sw+2) * (sh+2));
  196.  
  197. #define INDEX(x,y)    (((y)+1)*(sw+2) + (x) + 1)
  198.  
  199.     WM_ETARETI_BAG(scr->stacking_list, obj, iter) {
  200.  
  201.     while (obj) {
  202.         int x, y;
  203.  
  204.         if (iconPosition(obj, sx1, sy1, sx2, sy2, scr->current_workspace, 
  205.                  &x, &y)) {
  206.         int xdi, ydi;           /* rounded down */
  207.         int xui, yui;           /* rounded up */
  208.  
  209.         xdi = x/isize;
  210.         ydi = y/isize;
  211.         xui = (x+isize/2)/isize;
  212.         yui = (y+isize/2)/isize;
  213.         map[INDEX(xdi,ydi)] = 1;
  214.         map[INDEX(xdi,yui)] = 1;
  215.         map[INDEX(xui,ydi)] = 1;
  216.         map[INDEX(xui,yui)] = 1;
  217.         }
  218.         obj = obj->stacking->under;
  219.     }
  220.     }
  221.     /*
  222.      * Default position
  223.      */
  224.     *x_ret = 0;
  225.     *y_ret = 0;
  226.  
  227.     /*
  228.      * Look for an empty slot
  229.      */
  230.     for (si=0; si<sf; si++) {
  231.     for (pi=0; pi<pf; pi++) {
  232.         if (wPreferences.icon_yard & IY_VERT) {
  233.         x = xo + xs*(si*isize);
  234.         y = yo + ys*(pi*isize);
  235.         } else {
  236.         x = xo + xs*(pi*isize);
  237.         y = yo + ys*(si*isize);
  238.         }
  239.         if (!map[INDEX(x/isize, y/isize)]) {
  240.         *x_ret = x;
  241.         *y_ret = y;
  242.         done = 1;
  243.         break;
  244.         }
  245.     }
  246.     if (done)
  247.         break;
  248.     }
  249.  
  250.     free(map);
  251. }
  252.  
  253.  
  254. /*
  255.  * This function calculates the length of the intersection of two
  256.  * line sections. (Hey, is that english?)
  257.  */
  258. static int
  259. calcIntersectionLength(int p1, int l1, int p2, int l2)
  260. {
  261.     int isect;
  262.     int tmp;
  263.     
  264.     if (p1 > p2) {
  265.         tmp = p1;
  266.         p1 = p2;
  267.         p2 = tmp;
  268.         tmp = l1;
  269.         l1 = l2;
  270.         l2 = tmp;
  271.     }
  272.  
  273.     if (p1 + l1 < p2)
  274.         isect = 0;
  275.     else if (p2 + l2 < p1 + l1)
  276.         isect = l2;
  277.     else
  278.         isect = p1 + l1 - p2;
  279.         
  280.     return isect;
  281. }
  282.  
  283.  
  284. /*
  285.  * This function calculates the area of the intersection of two rectangles.
  286.  */
  287. static int
  288. calcIntersectionArea(int x1, int y1, int w1, int h1,
  289.                                 int x2, int y2, int w2, int h2)
  290. {
  291.     return calcIntersectionLength(x1, w1, x2, w2)
  292.            * calcIntersectionLength(y1, h1, y2, h2);
  293. }
  294.  
  295.  
  296. static int
  297. calcSumOfCoveredAreas(WWindow *wwin, int x, int y, int w, int h)
  298. {
  299.     int sum_isect = 0;
  300.     WWindow *test_window;
  301.     int tw,tx,ty,th;
  302.        
  303.     test_window = wwin->screen_ptr->focused_window;
  304.     for(;test_window != NULL && test_window->prev != NULL;)
  305.          test_window = test_window->prev;
  306.        
  307.     for(; test_window != NULL; test_window = test_window->next) {
  308.         if (test_window->frame->core->stacking->window_level
  309.             < WMNormalLevel) {
  310.             continue;
  311.         }
  312.  
  313. #if 0
  314.         tw = test_window->client.width;
  315.         if (test_window->flags.shaded)
  316.             th = test_window->frame->top_width;
  317.         else
  318.             th = test_window->client.height + extra_height;
  319. #else
  320.         tw = test_window->frame->core->width;
  321.         th = test_window->frame->core->height;
  322. #endif
  323.         tx = test_window->frame_x;
  324.         ty = test_window->frame_y;
  325.  
  326.         if (test_window->flags.mapped ||
  327.             (test_window->flags.shaded &&
  328.              !(test_window->flags.miniaturized ||
  329.                test_window->flags.hidden))) {
  330.             sum_isect += calcIntersectionArea(tx, ty, tw, th, x, y, w, h);
  331.         }
  332.     }
  333.     
  334.     return sum_isect;
  335. }
  336.  
  337.  
  338. static void
  339. smartPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
  340.                  unsigned int width, unsigned int height)
  341. {
  342.     WScreen *scr = wwin->screen_ptr;
  343.     int test_x = 0, test_y = Y_ORIGIN(scr);
  344.     int from_x, to_x, from_y, to_y;
  345.     int sx;
  346.     int min_isect, min_isect_x, min_isect_y;
  347.     int sum_isect;
  348.     int extra_height;
  349.     WArea usableArea = scr->totalUsableArea;
  350.  
  351.     if (wwin->frame)
  352.     extra_height = wwin->frame->top_width + wwin->frame->bottom_width;
  353.     else
  354.         extra_height = 24; /* random value */
  355.  
  356.     sx = X_ORIGIN(scr);
  357.  
  358.     min_isect = INT_MAX;
  359.     min_isect_x = sx;
  360.     min_isect_y = test_y;
  361.     
  362.     height += extra_height;
  363.  
  364.     while (((test_y + height) < usableArea.y2)) {
  365.  
  366.     test_x = sx;
  367.  
  368.     while ((test_x + width) < usableArea.x2) {
  369.  
  370.             sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
  371.                                               width, height);
  372.  
  373.         if ( sum_isect < min_isect ) {
  374.             min_isect = sum_isect;
  375.             min_isect_x = test_x;
  376.             min_isect_y = test_y;
  377.         }
  378.         
  379.         test_x += PLACETEST_HSTEP;
  380.     }
  381.     test_y += PLACETEST_VSTEP;
  382.     }
  383.  
  384.     from_x = min_isect_x - PLACETEST_HSTEP + 1;
  385.     from_x = WMAX(from_x, X_ORIGIN(scr));
  386.     to_x = min_isect_x + PLACETEST_HSTEP;
  387.     if (to_x + width > usableArea.x2)
  388.         to_x = usableArea.x2 - width;
  389.  
  390.     from_y = min_isect_y - PLACETEST_VSTEP + 1;
  391.     from_y = WMAX(from_y, Y_ORIGIN(scr));
  392.     to_y = min_isect_y + PLACETEST_VSTEP;
  393.     if (to_y + height > usableArea.y2)
  394.         to_y = usableArea.y2 - height;
  395.  
  396.     for (test_x = from_x; test_x < to_x; test_x++) {
  397.         for (test_y = from_y; test_y < to_y; test_y++) {
  398.             sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
  399.                                               width, height);
  400.  
  401.         if ( sum_isect < min_isect ) {
  402.             min_isect = sum_isect;
  403.             min_isect_x = test_x;
  404.             min_isect_y = test_y;
  405.         }
  406.         }
  407.     }
  408.  
  409.     *x_ret = min_isect_x;
  410.     *y_ret = min_isect_y;
  411. }
  412.  
  413.  
  414. static Bool
  415. autoPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
  416.                 unsigned int width, unsigned int height, int tryCount)
  417. {
  418.     WScreen *scr = wwin->screen_ptr;
  419.     int test_x = 0, test_y = Y_ORIGIN(scr);
  420.     int loc_ok = False, tw,tx,ty,th;
  421.     int swidth, sx;
  422.     WWindow *test_window;
  423.     int extra_height;
  424.     WArea usableArea = scr->totalUsableArea;
  425.     
  426.     if (wwin->frame)
  427.     extra_height = wwin->frame->top_width + wwin->frame->bottom_width + 2;
  428.     else
  429.     extra_height = 24; /* random value */
  430.  
  431.     swidth = usableArea.x2-usableArea.x1;
  432.     sx = X_ORIGIN(scr);
  433.  
  434.     /* this was based on fvwm2's smart placement */
  435.  
  436.     height += extra_height;
  437.  
  438.     while (((test_y + height) < (scr->scr_height)) && (!loc_ok)) {
  439.  
  440.     test_x = sx;
  441.  
  442.     while (((test_x + width) < swidth) && (!loc_ok)) {
  443.  
  444.         loc_ok = True;
  445.         test_window = scr->focused_window;
  446.  
  447.         while ((test_window != NULL) && (loc_ok == True)) {
  448.  
  449.         if (test_window->frame->core->stacking->window_level
  450.             < WMNormalLevel && tryCount > 0) {
  451.             test_window = test_window->next;
  452.             continue;
  453.         }
  454. #if 0
  455.                 tw = test_window->client.width;
  456.                 if (test_window->flags.shaded)
  457.                     th = test_window->frame->top_width;
  458.                 else
  459.                     th = test_window->client.height + extra_height;
  460. #else
  461.         tw = test_window->frame->core->width;
  462.         th = test_window->frame->core->height;
  463. #endif
  464.         tx = test_window->frame_x;
  465.         ty = test_window->frame_y;
  466.  
  467.         if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
  468.             (ty < (test_y + height)) && ((ty + th) > test_y) &&
  469.             (test_window->flags.mapped ||
  470.              (test_window->flags.shaded &&
  471.               !(test_window->flags.miniaturized ||
  472.             test_window->flags.hidden)))) {
  473.  
  474.                     loc_ok = False;
  475.         }
  476.         test_window = test_window->next;
  477.         }
  478.  
  479.         test_window = scr->focused_window;
  480.  
  481.         while ((test_window != NULL) && (loc_ok == True))  {
  482.  
  483.         if (test_window->frame->core->stacking->window_level
  484.             < WMNormalLevel && tryCount > 0) {
  485.             test_window = test_window->prev;
  486.             continue;
  487.         }
  488. #if 0
  489.                 tw = test_window->client.width;
  490.                 if (test_window->flags.shaded)
  491.                     th = test_window->frame->top_width;
  492.                 else
  493.                     th = test_window->client.height + extra_height;
  494. #else
  495.         tw = test_window->frame->core->width;
  496.         th = test_window->frame->core->height;
  497. #endif
  498.         tx = test_window->frame_x;
  499.         ty = test_window->frame_y;
  500.  
  501.         if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
  502.             (ty < (test_y + height)) && ((ty + th) > test_y) &&
  503.             (test_window->flags.mapped ||
  504.              (test_window->flags.shaded &&
  505.               !(test_window->flags.miniaturized ||
  506.             test_window->flags.hidden)))) {
  507.  
  508.                     loc_ok = False;
  509.         }
  510.         test_window = test_window->prev;
  511.         }
  512.         if (loc_ok == True) {
  513.         *x_ret = test_x;
  514.         *y_ret = test_y;
  515.         break;
  516.         }
  517.         test_x += PLACETEST_HSTEP;
  518.     }
  519.     test_y += PLACETEST_VSTEP;
  520.     }
  521.  
  522.     return loc_ok;
  523. }
  524.  
  525.  
  526. static void 
  527. cascadeWindow(WScreen *scr, WWindow *wwin, int *x_ret, int *y_ret,
  528.               unsigned int width, unsigned int height, int h)
  529. {
  530.     unsigned int extra_height;
  531.     WArea usableArea = scr->totalUsableArea;
  532.  
  533.     if (wwin->frame)
  534.     extra_height = wwin->frame->top_width + wwin->frame->bottom_width;
  535.     else
  536.     extra_height = 24; /* random value */
  537.     
  538.     *x_ret = h * scr->cascade_index + X_ORIGIN(scr);
  539.     *y_ret = h * scr->cascade_index + Y_ORIGIN(scr);
  540.     height += extra_height;
  541.  
  542.     if (width + *x_ret > usableArea.x2 || height + *y_ret > usableArea.y2) {
  543.     scr->cascade_index = 0;
  544.     *x_ret = X_ORIGIN(scr);
  545.     *y_ret = Y_ORIGIN(scr);
  546.     }
  547. }
  548.  
  549.  
  550. void
  551. PlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
  552.             unsigned width, unsigned height)
  553. {
  554.     WScreen *scr = wwin->screen_ptr;
  555.     int h = WMFontHeight(scr->title_font) + (wPreferences.window_title_clearance + TITLEBAR_EXTEND_SPACE) * 2;
  556.  
  557.     switch (wPreferences.window_placement) {
  558.      case WPM_MANUAL:
  559.     InteractivePlaceWindow(wwin, x_ret, y_ret, width, height);
  560.     break;
  561.  
  562.      case WPM_SMART:
  563.     smartPlaceWindow(wwin, x_ret, y_ret, width, height);
  564.     break;
  565.  
  566.      case WPM_AUTO:
  567.     if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 0)) {
  568.         break;
  569.     } else if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 1)) {
  570.         break;
  571.     }
  572.     /* there isn't a break here, because if we fail, it should fall
  573.        through to cascade placement, as people who want tiling want
  574.        automagicness aren't going to want to place their window */
  575.  
  576.      case WPM_CASCADE:
  577.         if (wPreferences.window_placement == WPM_AUTO)
  578.             scr->cascade_index++;
  579.  
  580.     cascadeWindow(scr, wwin, x_ret, y_ret, width, height, h);
  581.  
  582.         if (wPreferences.window_placement == WPM_CASCADE)
  583.             scr->cascade_index++;
  584.     break;
  585.  
  586.      case WPM_RANDOM:
  587.     {
  588.         int w, h, extra_height;
  589.         WArea usableArea = scr->totalUsableArea;
  590.  
  591.             if (wwin->frame)
  592.                 extra_height = wwin->frame->top_width + wwin->frame->bottom_width + 2;
  593.             else
  594.                 extra_height = 24; /* random value */
  595.  
  596.         w = ((usableArea.x2-X_ORIGIN(scr)) - width);
  597.         h = ((usableArea.y2-Y_ORIGIN(scr)) - height - extra_height);
  598.         if (w<1) w = 1;
  599.         if (h<1) h = 1;
  600.         *x_ret = X_ORIGIN(scr) + rand()%w;
  601.         *y_ret = Y_ORIGIN(scr) + rand()%h;
  602.     }
  603.     break;
  604.  
  605. #ifdef DEBUG    
  606.      default:
  607.     puts("Invalid window placement!!!");
  608.     *x_ret = 0;
  609.     *y_ret = 0;
  610. #endif
  611.     }
  612.     
  613.     if (*x_ret + width > scr->scr_width)
  614.     *x_ret = scr->scr_width - width;
  615.     if (*x_ret < 0)
  616.         *x_ret = 0;
  617.  
  618.     if (*y_ret + height > scr->scr_height)
  619.     *y_ret = scr->scr_height - height;
  620.     if (*y_ret < 0)
  621.         *y_ret = 0;
  622. }
  623.  
  624.  
  625.